package fr.lip6.meta.ple.generation.fh.java;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import artefact.generic.ConstructionPrimitive;
import artefact.umlClassDiagram.CreateAttribute;
import artefact.umlClassDiagram.CreateClass;
import artefact.umlClassDiagram.CreateGeneralization;
import artefact.umlClassDiagram.CreateInterface;
import artefact.umlClassDiagram.CreateOperation;
import artefact.umlClassDiagram.CreateOperationRefinement;

import artefact.umlClassDiagram.CreatePackage;
import artefact.umlClassDiagram.impl.CreateAttributeImpl;
import fr.lip6.meta.ple.constraintsextraction.constraint.Constraint;
import fr.lip6.meta.ple.featureIdentification.Feature;
import fr.lip6.meta.ple.featureIdentification.Product;
import fr.lip6.meta.ple.generation.strategy.ProductLineConstructionStrategy;
import fr.lip6.meta.ple.generation.strategy.ReadXmiStrategy;
import fr.lip6.meta.ple.generation.tools.PLConstructionUtil;

import fr.lip6.meta.tools.StatementPrimitive;
import fr.lip6.meta.tools.Trigger;

public class ProductLineConstructionFHJava implements ProductLineConstructionStrategy 
{
	static String rep = "./SPL-Construction-FH-Java/features";
	private PLConstructionFactoryFHJava factory = new PLConstructionFactoryFHJava(rep);
	protected Collection<Product> products = null;

	@Override
	public void generateFeatureModel(Collection<Feature> features, 
			ArrayList<Constraint> rules,String model_dir) {
		PrintStream out;
		String rep1 = model_dir;
		File repit = new File (rep1);
		deleteRecursive(repit);
		repit.mkdirs();

		File dotFile = new File(rep1+"model.xml");
		FileOutputStream fout;

		try {
			fout = new FileOutputStream(dotFile);
			out = new PrintStream(fout);

			out.print("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+"\n");
			out.println("<featureModel>");
			out.println("<struct>");
			out.println("<and abstract=\"true\" mandatory=\"true\" name=\"SPLConstruction\">");
			//	out.println("<feature mandatory=\"true\" name=\"F0\"/>");

			for (Feature ancestorFeat : features) {
				String fname[] = ancestorFeat.getId().split("_");
				boolean isAncestorFeatMandatory = PLConstructionUtil.isMandatory(ancestorFeat, products);

				if (fname.length == 1) {
					ArrayList<Feature> allsubf = getsubfeaturelevel1(ancestorFeat.getId(), features);

					if (allsubf.size() > 0) {
						if (isAncestorFeatMandatory) {
							out.println("<and mandatory=\"true\" name=\""+ancestorFeat.getId()+"\""+" >");
						}
						else {
							out.println("<and name=\""+ancestorFeat.getId()+"\""+" >");
						}
						for(Feature f1 : allsubf) {
							//out.println("<alt mandatory=\"true\" name=\""+f1.getName()+"\" >");
							ArrayList<Feature> allsubf1 = getSubfeaturelevel2(f1.getId(), features);
							boolean isF1Mandatory;

							if (allsubf1.size() > 0) {
								Map<Integer, Product> subFeatProducts = new HashMap<Integer, Product>();
								
								for(Feature s1 : allsubf1) {
									subFeatProducts.putAll(PLConstructionUtil.getFeatureProducts(s1, products));
								}
								
								if (subFeatProducts.size() == products.size()) // number of key-value mappings
									isF1Mandatory = true;
								else
									isF1Mandatory = false;
								
								out.println(
										"<alt abstract=\"true\" "
									+	(isF1Mandatory ? "mandatory=\"true\" " : "")
									+	"name=\""+f1.getName()+"\" >"+"\n");

								for(Feature s1 : allsubf1) {
									boolean isS1Mandatory = PLConstructionUtil.isMandatoryChild(s1, f1, products);
									out.println(
											"<feature "
										+	(isS1Mandatory ? "mandatory=\"true\" " : "")
										+	"name=\""+s1.getName()+"\""+" />");
								}

								out.println("</alt>"+"\n");
							} else {
								isF1Mandatory = PLConstructionUtil.isMandatoryChild(f1, ancestorFeat, products);
								out.println("<feature "
										+	(isF1Mandatory ? "mandatory=\"true\" " : "")
										+	"name=\""+f1.getName()+"\" />"+"\n");
							}
						}
						out.println("</and>");	
					} else {
						out.println("<feature "
								+	(isAncestorFeatMandatory ? "mandatory=\"true\" " : "")
								+	"name="+"\""+ancestorFeat.getId()+"\""+" />");
					}
				}
			}

			out.println("</and>");
			out.println("</struct>");
			out.println("<constraints>");

			//System.out.println("########CONSTRAINTTTTTTT#######"+rules.size());
			for(Constraint rule : rules){
				out.println("<rule>");
				out.println("<eq>");



				out.println(rule.getLeft().toXML());

				out.println(rule.getRight().toXML());
				out.println("</eq>");
				out.println("</rule>");
			}


			out.println("</constraints>");
			out.println("<comments/>");

			out.println(	"</featureModel>");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}

	@Override
	public void construct(Collection<Feature> features, Collection<Product> products) {
		for(Feature f : features) {
			//System.out.println("ProductLineConstructionFHJava.construct():\n"+f);
			try {
				generateClasses(f);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

	public void generateClasses(Feature f) throws FileNotFoundException {
		ArrayList<String> classnames = new ArrayList<String>();
		//ArrayList<String> attributnames = new ArrayList<String>();
		//ArrayList<String> operationsnames = new ArrayList<String>();
		File repit = new File (rep);
		File fo;
		String featName;

		repit.mkdirs();

		if (f.getId().split("_").length > 1) {
			fo = new File(rep+"/"+f.getName());
			featName = f.getName();
		} else {
			fo = new File(rep+"/"+f.getId());
			featName = f.getId();
		}

		fo.mkdir();

		//System.err.println("Generate SOURCE FILES: feature name: "+fo.getAbsolutePath());

		for(Trigger t : f) {
			StatementPrimitive st = (StatementPrimitive) t;
			ConstructionPrimitive consPrimitive = st.getPrimitive();
			PrintStream out;
			boolean consPrimitiveTreated = false;
			
			//System.out.println("Classes: "+PLConstructionFactoryFHJava.allclasss);
			//System.out.println("Feature classes: "+classnames);
			//System.out.println("Feature: "+featName);
			//System.out.println("Construction primitive: "+consPrimitive);

			if (consPrimitive instanceof CreatePackage) {
				//System.out.println("[[ProductLineConstructionFHJava.generate(): CREATE PACKAGE");
				out = factory.createPackage(st, f, classnames);
				//System.out.println("ProductLineConstructionFHJava.generate(): CREATE PACKAGE]]]");
				out.close();
				consPrimitiveTreated = true;
			}

			if (consPrimitive instanceof CreateClass) {
				CreateClass c = (CreateClass) st.getPrimitive();

				if (!(classnames.contains(c.getName()))) {
					//System.out.println("[[ProductLineConstructionFHJava.generate(): CREATE CLASS");
					out = factory.createClass(c, f, classnames);
					//System.out.println("ProductLineConstructionFHJava.generate(): CREATE CLASS]]]");
					out.close();
					consPrimitiveTreated = true;
				}
			}

			if (consPrimitive instanceof CreateGeneralization) {
				CreateGeneralization c = (CreateGeneralization) st.getPrimitive();

				if (!(classnames.contains(c.getSub()))) {
					//System.out.println("[[ProductLineConstructionFHJava.generate(): CREATE GENERALIZATION");
					out = factory.createGeneralization(c, f, classnames);
					//System.out.println("ProductLineConstructionFHJava.generate(): CREATE GENERALIZATION]]]");
					out.close();
					consPrimitiveTreated = true;
				}
			}
			
			if (consPrimitive instanceof CreateInterface) {
				CreateInterface c = (CreateInterface) st.getPrimitive();
				
				if (!(classnames.contains(c.getName()))) {
					//System.out.println("[[ProductLineConstructionFHJava.generate(): CREATE INTERFACE");
					out = factory.createInterface(c, f, classnames);
					//System.out.println("ProductLineConstructionFHJava.generate(): CREATE INTERFACE]]]");
					out.close();
					consPrimitiveTreated = true;
				}
			}
			
			if (consPrimitive instanceof CreateAttribute ) {
				CreateAttributeImpl o = (CreateAttributeImpl) st.getPrimitive();

				if (!(classnames.contains(o.getOwner()))) {
					//System.out.println("[[ProductLineConstructionFHJava.generate(): CREATE ATTRIBUTE");
					out = factory.createAttribute(o, f, classnames);
					//System.out.println("ProductLineConstructionFHJava.generate(): CREATE ATTRIBUTE]]]");
					out.close();
					consPrimitiveTreated = true;
				}
			}

			if (consPrimitive instanceof CreateOperation) {
				CreateOperation o = (CreateOperation) st.getPrimitive();

				if (!(classnames.contains(o.getOwener()))) {
					//System.out.println("[[ProductLineConstructionFHJava.generate(): CREATE OPERATION");
					out = factory.createOperation(o, f, classnames);
					//System.out.println("ProductLineConstructionFHJava.generate(): CREATE OPERATION]]]");
					out.close();
					consPrimitiveTreated = true;
				}
			}

			if (consPrimitive instanceof CreateOperationRefinement) {
				CreateOperationRefinement o = (CreateOperationRefinement) st.getPrimitive();
				
				if (!(classnames.contains(o.getOwner()))) {
					classnames.add(o.getOwner());
					//System.out.println("[[ProductLineConstructionFHJava.generate(): CREATE OPERATION REFINEMENT");
					out = factory.createOperationRefinement(o, f, classnames);
					//System.out.println("ProductLineConstructionFHJava.generate(): CREATE OPERATION REFINEMENT]]]");
					out.close();
					consPrimitiveTreated = true;
				}
			}
			
			//if (consPrimitiveTreated) {
			//	System.out.println(		"ProductLineConstructionFHJava.generate(): written in file: "
			//							+	factory.getPrintFile().getDotFile().getPath()	);
			//}
		}
	}

	public static ArrayList<Feature> getsubfeaturelevel1(String fatherid, Collection<Feature> features) {
		ArrayList<Feature> allsubfeatures = new ArrayList<Feature>();

		for (Iterator<Feature> iter1 = features.iterator(); iter1.hasNext();) { 	
			Feature f = iter1.next();

			String name[] = f.getId().split("_");
			if (name.length==2) {

				if (name[0].equals(fatherid)) {
					allsubfeatures.add(f);
				}
			}
		}
		
		return allsubfeatures;
	}


	public static ArrayList<Feature> getSubfeaturelevel2(String fatherid, Collection<Feature> features) {
		ArrayList<Feature> allsubfeatures = new ArrayList<Feature>();

		for (Iterator<Feature> iter1 = features.iterator(); iter1.hasNext();) { 	
			Feature f = iter1.next();
			String fathername[] = fatherid.split("_");
			String res = fathername[0]+fathername[1];

			String name[] = f.getId().split("_");
			if (name.length==3) {
				String s = name[0]+name[1];
				if (s.equals(res)) {
					allsubfeatures.add(f);
				}
			}

		}

		return allsubfeatures;
	}

	public static void deleteRecursive(File f)  {
		if (f.exists()){
			if (f.isDirectory()){
				File[] childs = f.listFiles();
				int i = 0;
				for(i = 0; i<childs.length; i++){
					deleteRecursive(childs[i]);
				}

			}
			f.delete();
		}
	}

	// getters and setters //

	/* (non-Javadoc)
	 * @see fr.lip6.meta.ple.generation.strategy.ProductLineConstructionStrategy#setProducts(java.util.Collection)
	 */
	@Override
	public void setProducts(Collection<Product> products) {
		this.products = products;
	}

	/**
	 * @param readXmi
	 */
	public void setReadXmi(ReadXmiStrategy readXmi) {
		this.factory.setReadXmi(readXmi);
	}
}
